import struct, binascii
import random

from bitstring import BitArray

from jt808log import log


def send_7e7d(bin_str):

	rst = bin_str.replace(b'}', b'}\x02')
	rst = rst.replace(b'~', b'}\x02')

	return rst

def recv_7e7d(bin_str):
	
	rst = bin_str.replace( b'}\x02',  b'}')
	rst = rst.replace(b'}\x02', b'~')

	return rst

# def bcd_decode2str(termPhone_tuple):

# 	termPhone_int = []
# 	for x in termPhone_tuple:
# 		a = int(bin(x & 0xf0), 2) >> 4
# 		b = int(bin(x & 0x0f), 2)
# 		termPhone_int.append(str(a))
# 		termPhone_int.append(str(b))

# 	return (''.join(termPhone_int)).strip('0')

#解码成SIM字符串
def bcd_unpack_str(bcdarray):
	return ( ''.join(['%02x'% c for c in bcdarray]) ).strip('0')	#去掉开头结尾字符 0

#编码成6个10进制数列表
def str_packed_bcd(number):
	numtest = '%012d'% int(number)
	return [(ord(numtest[x])-ord('0'))<<4 | (ord(numtest[x+1])-ord('0')) for x in range(0, len(numtest), 2)]

#接收原始数据后计算返回 byte校验码
#  msg： b'~\x80\x01\x00\x05\x06H84!4\x00\x00\x00\x00\x01\x02\x00\xd0~' -> 校验码：$
#  msg 必须是python字节码流 ，不能是十六进制的ascii，如 7e
def check_code(msg):
	# msg 可以struct.pack()获取
	ascii_data = msg[1:-2]
	m_len = len(ascii_data)
	tmp_check = 0
	for t in range(m_len):
		tmp_check ^= ascii_data[t]

	pack_tmp_check = struct.pack('>B', tmp_check)
	# log.info('check_code: %s, 十进制check_code: %d' % (pack_tmp_check, tmp_check))
	return pack_tmp_check

#
def make_auth():
    result_list = []
    for i in range(0,8):
        temp = random.randint(1, 9)
        result_list.append(ord(str(temp)))
    return result_list

# time_str = '190726143544'
def to_datetime(strx=''):
	date = strx[0:6]  # Example '190726'
	the_time = strx[6:12]  # Example '144515'
	sub_date = '20'
	sub_time = ' '

	for x in range(0, len(date), 2):
		sub_date += date[x:x+2] +'-'

	for x in range(0, len(the_time), 2):
		sub_time += the_time[x:x+2] +':'

	# sub_time = ' 11:40:19'
	datetime = sub_date.rstrip('-') + sub_time.rstrip(':')
	# datetime = 2016-1-5 11:40:19'
	return datetime


#获取数据的commandId
def get_commandId(data):

	# 去掉原始数据头标识
	content = data[1:-1]
	commandId = struct.unpack('>H', content[:2])[0]	#十进制
	commandId = '%04x' % commandId 	#十六进制

	return commandId


#根据 十进制<commandId> 获取对应的类,返回 类的实例
#msg_ins = getclass(256)
def getMsgClass(commandId):
	if commandId in MSGS:
		return MSGS[commandId]()
	else:
		return


#消息类 Message
class Message(object):

	commandId = None
	# seqNum = 0
	baocount = 1
	baoid = 1
	fenbaoLen = 1000 #最大值1023
	#编码属性
	flagBit =  struct.pack('>B',0x7e)
	# termPhone = ''
	structParams = []

	def __init__(self, termPhone=None, seqNum=0, **kwargs):

		self.body_Length = 0
		self.seqNum = seqNum
		self.termPhone = termPhone

		for key, value in kwargs.items():
			setattr(self, key, value)

		for sParam in self.structParams:
			setattr(self, sParam[0], sParam[2])


	# 类实例属性编码成 Raw
	def generateData(self):
		
		rst = []
		body = b''
		reps_ins = self.requireAck()

		# structParams = [('Seq','>H',0), ('MsgID','>H',0), ('Result','>B',0)]
		for dParams in reps_ins.structParams:
			# print(getattr(reps_ins, dParams[0]))
			fmt = dParams[1]
			if dParams[0] == 'Auth':
				body = body + struct.pack(fmt, *make_auth())
				print(getattr(reps_ins, dParams[0]))
				continue
		
			body = body + struct.pack( fmt, getattr(reps_ins, dParams[0]) )
		
		jiami = 0 
		fenbao = 0
		baocount = 1

		bodylen = self.fenbaoLen
		msgprop = struct.pack('>H', BitArray('0b00%s00%s, uint:10=%s' % (fenbao,jiami,bodylen)).uint)
		bcd_sim = struct.pack('>6B', *str_packed_bcd(self.termPhone))
		header = struct.pack('>H', reps_ins.commandId) + msgprop + bcd_sim + struct.pack('>H', self.seqNum)
		msgbody = body
		check = check_code(header + msgbody)

		rst.append(self.flagBit + header + msgbody + check + self.flagBit)
		return rst[0]


	# 接收 Raw数据解析成类实例属性
	@staticmethod
	def pasrseMsg(data=None):
		dlen = len(data)
		if dlen > 0:
			commandId = get_commandId(data)	#十六进制数字
		else:
			commandId = 0x8100
			
		pos = 0
		dataLen = len(data)
		content = data[1:dataLen-1]
		msg_ins = getMsgClass(commandId)

		if msg_ins:

			''' 1 消息头信息解析'''
			# 无分包时 消息头长度 12Bytes
			pos = 12	
			
			# 消息体属性 解析
			msg_ins.msgProp = struct.unpack('>H', content[2:4])[0]
			msg_ins.msgProp_bitArray = BitArray('uint:16=%s' % msg_ins.msgProp).bin
			msg_ins.msgProp_subPackage = int(msg_ins.msgProp_bitArray[2:3])
			msg_ins.msgProp_encrypt = int(msg_ins.msgProp_bitArray[5:6])
			msg_ins.msgProp_bodyLength = int(msg_ins.msgProp_bitArray[7:], 2)
			log.info('subPackage: %d, encrypt: %d, Length: %d' % (msg_ins.msgProp_subPackage, msg_ins.msgProp_encrypt, msg_ins.msgProp_bodyLength))
			
			# 手机号 解析
			msg_ins.termPhone_tuple = struct.unpack('>6B', content[4:10])
			msg_ins.termPhone = bcd_unpack_str(msg_ins.termPhone_tuple)
			log.info('Sim: %s' % msg_ins.termPhone)
			
			# 消息流水号 解析
			msg_ins.seq = int(struct.unpack('>H', content[10:12])[0])
			log.info('seq: %s' % msg_ins.seq)

			# 消息体属性 若分包 解析分包数、分包id 
			if  msg_ins.msgProp_subPackage:
				pos = 16
				msg_ins.msg_Package_totle, msg_ins.msg_Package_id = struct.unpack('>2H', content[12:16])
				log.info('Package: totle %d, id %d' % (msg_ins.msg_Package_totle, msg_ins.msg_Package_id))
			

			''' 2 消息体内容解析'''
			# structParams = [('Seq','>H',0), ('MsgID','>H',0), ('Result','>B',0)]
			for sParam in msg_ins.structParams:
				fmt = sParam[1]

				# BCD
				if type(sParam[2] == 'BCD'):
					s_len = struct.calcsize(fmt)
					tmp_data = content[pos: pos+s_len]
					setattr( msg_ins, sParam[0], bcd_unpack_str(struct.unpack(fmt, tmp_data)) )		#????
					pos += s_len
					continue

				# strings
				if type(sParam[2]) == str:
					str_len = struct.calcsize(fmt)
					tmp_data = content[pos: pos+str_len]
					# log.info('tmp_data: %s' % str(tmp_data))
					setattr(msg_ins, sParam[0], tmp_data[0:8])	#??? 截取的是终端ID
					pos += str_len
					continue

				setattr( msg_ins, sParam[0], struct.unpack(fmt, content[pos:pos+struct.calcsize(fmt)])[0])
				pos += struct.calcsize(fmt)

			# 接收的数据 保存为类实例属性返回 
			return msg_ins


	def __repr__(self):
		##
		r = "Message--commandId:%04x--seq:%s--" % (self.commandId, self.seq)
		for sParam in self.structParams:
			if sParam[2] == 'r':
				r += "%s:%r--" % (sParam[0], getattr(self, sParam[0], 0))
			elif sParam[0] == 'MsgID':
				r += "%s:%x--" % (sParam[0], getattr(self, sParam[0], 0))
			else:
				tmpdata = getattr(self, sParam[0], 0)
				r += "%s:%s--" % (sParam[0], tmpdata )

		return r


#请求基类
class MessageRequest(Message):
	pass
	requireAck = None

#应答基类
class MessageResponse(Message):
	pass
	# requireAck = None


#终端注册应答 <8100>
class JT808_termreg_resp(MessageResponse):
	commandId = 0x8100
	structParams = [
		('Seq','>H',0),
		('Result','>B',0),
		('Auth','>8B',''),
	]


#终端注册 <0100>
class JT808_term_reg(MessageRequest):
	requireAck = JT808_termreg_resp
	commandId = 0x0100
	structParams = [
		('Prov','>H',0),
		('City','>H',0),
		('ManuId','>5B',''),
		('TermType','>20B',''),
		('TermID','>7B',''),
		('VehicleColor', '>B', 0),
		('VehicleNum','>8B',''),
	]


#终端通用应答 <0001>
class JT808_term_resp(MessageResponse):
	commandId = 0x0001
	structParams = [
		('Seq','>H',0),
		('MsgID','>H',0),
		('Result','>B',0),
	]


#平台通用应答 <8001>
class JT808_plat_resp(MessageResponse):
	commandId = 0x8001
	structParams = [
		('Seq','>H',0),
		('MsgID','>H',0),	#对应终端发上来的 commandId
		('Result','>B',0),
	]

    
#终端心跳 <0002>
class JT808_term_heart(MessageRequest):
	requireAck = JT808_plat_resp
	commandId = 0x0002    


#终端注销 <0003>
class JT808_term_unreg(MessageRequest):
	requireAck = JT808_plat_resp
	commandId = 0x0003

   
#终端鉴权 <0102>
class JT808_term_auth(MessageRequest):
	requireAck = JT808_plat_resp
	commandId = 0x0102
	structParams = [
		('Auth','>8B',''),
	]


#位置信息汇报 <0200>
class JT808_term_loc(MessageRequest):
	requireAck = JT808_plat_resp
	commandId = 0x0200
	structParams = [
		('SOS','>L',0),
		('Status','>L',0),
		('Lat','>L',0),
		('Lng','>L',0),
		('Height','>H',0),
		('Speed','>H',0),
		('Direction','>H',0),
		('Time','>6B','BCD'),
		# 位置附加信息项，附加信息长度id、长度根据jt808表27定义
		('AddMsgId','>B',''),
		('AddMsgLen','>B',''),
		('AddMsgNote','>4B','')		# 默认解析里程
	]

	structParams_add = [
		('0x01','>L',0),	#里程
		('0x02','>H',0),	#油量
		('0x03','>H',0),	#行驶记录仪速度
		('0xFF','>500B',0),	#自定义

	]


#文本信息下发
class JT808_plat_sendtxt(MessageRequest):
	requireAck = JT808_term_resp
	commandId = 0x8300
	structParams = [
	    ('Flag','>B',0),
	    ('Content','>*B',''),
	]


#根据commandId 获取类名
MSGS = {'0100':JT808_term_reg,		# 终端注册
		'8100':JT808_plat_resp,		# 注册应答
		'0102':JT808_term_auth,		# 终端鉴权
		'8001':JT808_plat_resp,		# 平台通用应答 
		'0002':JT808_term_heart,	# 终端心跳
		'0003':JT808_term_unreg,	# 终端注销
		'0001':JT808_term_resp,		# 终端通用应答
		'0200':JT808_term_loc,		# 终端位置
		}



if __name__ == '__main__':

	# 终端这车 Raw
	data = b'~\x01\x00\x00-\x01p\x04\x02\x163\x00\x01\x00,\x00\x0170111BSJ_A6BD\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x009471058\x01\xd5\xe3B0R931\x0b~'
	# 终端鉴权 Raw 
	data2 =  b"~\x02\x00\x00&\x01D!W\x85\x88\x00\x03\x00\x00\x00\x00\x00\x08\x10\x03\x02b\x04U\x06\xefL=\x01\xf4\x02X\x00\x00\x19\x07\x11\x12\x12Q\x01\x04\x00\x00'$\x02\x02\x00x$~"

	log.info('Raw_data: %s \n' % data2)
	log.info('Hex_data：%s \n' % binascii.hexlify(data2))

	# 判断数据合法性
	msg_ins = Message.pasrseMsg(data2)
	log.info(msg_ins)

	# 组装平台应答消息  根据 requireAck （消息头一样 ，消息体不同）
	rst = []
	if True:
		rst = msg_ins.generateData()
	
	log.info('resp_data: %s' % rst)

	# print(check_code(data2))

